#include "HyperTextTransfer.h"
#include "Socket.h"
#include "hl_hashwrapper.h"
#include "hl_md5wrapper.h"

HyperTextTransfer::HyperTextTransfer()
{
	cout << "[HyperTextTransfer] Constructor Called" << endl;
}

HyperTextTransfer::~HyperTextTransfer()
{
	cout << "[HyperTextTransfer] Destructor Called" << endl;
}

char * HyperTextTransfer::GetFilename(char * path) {

	char *	ptr = strrchr(path, '/');
	if (ptr != NULL) {
		ptr++;
	}
	return ptr;

}

void HyperTextTransfer::ParseUrl(char * url, UrlStruct * ret) {

	char *tmp = (char *)strdup(url);
	int len = 0;

	ret->protocol = (char *) strtok(tmp, "/");
	len = strlen(ret->protocol) + 2;
	ret->host = (char *) strtok(NULL, "/");
	len += strlen(ret->host);

	ret->path = (char *)strdup(&url[len]);
	ret->path = (char *) strtok(ret->path, "#");
	ret->protocol = (char *) strtok(ret->protocol, ":");

	ret->host = (char *) strtok(ret->host, ":");
	tmp = (char *) strtok(NULL, ":");

	if(tmp == NULL) {
		if(strcmp(ret->protocol, "http") == 0) {
			ret->port = 80;
		} else if(strcmp(ret->protocol, "https") == 0) {
			ret->port = 443;
		}
	} else {
		ret->port = atoi(tmp);
	}
	
	ret->file = GetFilename(ret->path);

}

char * HyperTextTransfer::ResolveHostname(char * host) {

	hostent * record = gethostbyname(host);
	if(record == NULL) {
		cout << host << " is unavailable" << endl;
		return (char *)NULL;
	}
	in_addr * address = (in_addr * )record->h_addr;
	return (char *)inet_ntoa(* address);
}

UrlStruct HyperTextTransfer::PerformDownload(char * uName) {

        hashwrapper *		Md5HashWrap = new md5wrapper();
	UrlStruct		UrlFileInfo;
	ofstream		DownloadFile;
	ParseUrl(uName, &UrlFileInfo);

	TCPSocket	sock(ResolveHostname(UrlFileInfo.host), UrlFileInfo.port);
	char *		m_pBuf		= new char[1024];

	ulong	BufferWorking	= 0;
	int	BufferCursor	= 0;
	int	BufferSize	= 0;
        int	BytesRead	= 0;
	int	fCount		= 0;
	bool	wStream		= false;


		memset(m_pBuf, 0x0, strlen(m_pBuf));
	        sprintf(m_pBuf, "GET %s HTTP/1.1\nHost: %s\nConnection: close\n\n", UrlFileInfo.path, UrlFileInfo.host);
	        sock.send(m_pBuf, strlen(m_pBuf));
	        memset(m_pBuf, 0x0, strlen(m_pBuf));

	        wStream = false;
	        while ((BytesRead = (sock.recv(m_pBuf, 1024))) > 0) {

			for (BufferCursor = 0; BufferCursor != BytesRead; BufferCursor++, BufferWorking++) {

				if (wStream == true) {
				        /** write content to disc */
					DownloadFile.put(m_pBuf[BufferCursor]);
				}

				/** if we could extract the http header information for the `Content-Length`, we could provide a progress bar */
			        switch(m_pBuf[BufferCursor]) {
					case 0x0A:
					case 0x00:
					        fCount++;
					case 0x0D:

					        if ((fCount > 1) && (wStream == false)) {
					                BufferWorking = -1;
					                DownloadFile.open(UrlFileInfo.file, ios::out | ios::trunc | ios::binary);
					                if (!DownloadFile.is_open()) {
					                        wStream = false;
					                        cout << "Error: Output file could not be written to - " << UrlFileInfo.file << endl;
					                } else {
					                        wStream = true;
					                        cout << uName << endl << "Download in progress, please wait..." << endl;
					                }
					        }
					        break;

					default:
					        fCount = 0;
					        break;
			        }

			}

			memset(m_pBuf, 0x0, strlen(m_pBuf));
	        }

		if (wStream == true)
		{
			DownloadFile.close();
			UrlFileInfo.size = BufferWorking;
			try {
			        UrlFileInfo.md5 = (char *)Md5HashWrap->getHashFromFile(UrlFileInfo.file).c_str();
			} catch(hlException &e) {
				cout << "Error(" << e.error_number() << "): " << e.erro_message() << endl;
			}

		}

	        delete Md5HashWrap;
	        free(m_pBuf);
	        sock.CloseSocket();

	        return UrlFileInfo;

}
